
// author: niXman
// e-mail: i.nixman@gmail.com
// date: 28.05.2011
//
//
// Boost Software License - Version 1.0 - August 17th, 2003
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#ifndef _netclasses_acceptor_hpp_included_
#define _netclasses_acceptor_hpp_included_

#include <netclasses/socket.hpp>

namespace netclasses {

/***************************************************************************/

struct acceptor: private boost::noncopyable {
   acceptor(boost::asio::io_service& ios, const std::string& ip, boost::uint16_t port)
      :acc(ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(ip), port)),
      run(true)
   {}

   boost::asio::io_service& get_io_service() { return acc.get_io_service(); }

   void cancel() { acc.cancel(); }
   void cancel(boost::system::error_code& ec) { acc.cancel(ec); }
   void close() { acc.close(); }
   void close(boost::system::error_code& ec) { acc.close(ec); }

   void accept(socket& socket, boost::asio::ip::tcp::endpoint& ep, boost::system::error_code& ec) {
      acc.accept(socket.get_socket(), ep, ec);
   }

   socket_ptr accept(boost::asio::ip::tcp::endpoint& ep, boost::system::error_code& ec) {
      socket_ptr socket(new socket_ptr::element_type(acc.get_io_service()));
      acc.accept(socket->get_socket(), ep, ec);
      return socket;
   }

   void async_accept(boost::function<void(socket_ptr, const boost::asio::ip::tcp::endpoint&, const boost::system::error_code&)> handler) {
      run = true;
      socket_ptr socket(new socket_ptr::element_type(acc.get_io_service()));
      acc.async_accept(
         socket->get_socket(),
         boost::bind(
            &acceptor::private_async_accept,
            this,
            socket,
            handler,
            boost::asio::placeholders::error
         )
      );
   }

   void async_accept(void(*handler)(socket_ptr, const boost::asio::ip::tcp::endpoint&, const boost::system::error_code&)) {
      async_accept(boost::bind(handler, _1, _2, _3));
   }

   template<typename Obj>
   void async_accept(Obj* obj, void(Obj::*handler)(socket_ptr, const boost::asio::ip::tcp::endpoint&, const boost::system::error_code&)) {
      async_accept(boost::bind(handler, obj, _1, _2, _3));
   }

   template<typename Obj>
   void async_accept(boost::shared_ptr<Obj> obj, void(Obj::*handler)(socket_ptr, const boost::asio::ip::tcp::endpoint&, const boost::system::error_code&)) {
      async_accept(boost::bind(handler, obj, _1, _2, _3));
   }

   void stop_accept() { run = false; }

private:
   void private_async_accept(
      socket_ptr socket,
      boost::function<void(socket_ptr, const boost::asio::ip::tcp::endpoint&, const boost::system::error_code&)> handler,
      const boost::system::error_code& ec)
   {
      handler(socket, socket->get_socket().remote_endpoint(), ec);
      if ( run ) {
         async_accept(handler);
      }
   }

private:
   boost::asio::ip::tcp::acceptor acc;
   bool run;
};

/***************************************************************************/

} // namespace netclasses

#endif // _netclasses_acceptor_hpp_included_
